/*
 * serial.c
 *
 *  Created on: 2017年3月28日
 *      Author: chenshisheng
 */

#include "serial.h"
#include "uart.h"
#include "etimer.h"
//#include "active.h"
#include "syscfg.h"
#include "shell.h"

#include <stdarg.h>
#include <string.h>

#define ENABLE_AUTO_NEW_LINE 1

#define SERIAL_FLAG_TESTING 0x01

// 串口收发控制结构
typedef struct
{
    UART_T *uart;                          // 标准库函数操作串口所用的句柄
    rbuf_t rxRingBuffer;                   // 接收队列
    uint8_t _rxBuf[SERIAL_RX_BUFFER_SIZE]; // 接收缓存
    rbuf_t txRingBuffer;                   // 发送队列
    uint8_t _txBuf[SERIAL_TX_BUFFER_SIZE]; // 发送缓存
    Serial_Callback_t onTxBegin;           // 开始发送数据时的调用的回调函数
    Serial_Callback_t onTxComplete;
    Serial_RxByteCallback_t onRxByte;      // 接收到一个字节时调用的回调函数
    uint32_t flags;
#ifdef CMSIS_OS
    osSemaphoreId_t rxSem;
#endif
}Serial_Ctrl_t;

typedef struct
{
    UART_T *uart;
    const char *name;
    void (*init)(void);
}Serial_Config_t;

struct income
{
    Serial_t serial;
    uint8_t  byte;
};

// 串口序号对应的标准库函数操作串口所用的句柄
static const Serial_Config_t _configs[Serial_Max] =
{
#if SERIAL_HAVE_UART0
        {UART0, "Serial_0", SysCfg_Uart0},
#endif

#if SERIAL_HAVE_UART1
        {UART1, "Serial_1", SysCfg_Uart1},
#endif

#if SERIAL_HAVE_UART2
        {UART2, "Serial_2", SysCfg_Uart2},
#endif

#if SERIAL_HAVE_UART3
        {UART3, "Serial_3", SysCfg_Uart3},
#endif

#if SERIAL_HAVE_UART4
        {UART4,  "Serial_4", SysCfg_Uart4},
#endif

#if SERIAL_HAVE_UART5
        {UART5,  "Serial_5", SysCfg_Uart5},
#endif

#if SERIAL_HAVE_UART6
        {UART6, "Serial_6", SysCfg_Uart6},
#endif

#if SERIAL_HAVE_LPUART1
        {LPUART1, "Serial_LP1", SysCfg_LpUart1},
#endif
};

static Serial_Ctrl_t _ctrls[Serial_Max];
static unsigned int _actionBits;

static void _DefaultCB(Serial_t serial);
static bool _DefaultRxByteCB(Serial_t serial, uint8_t b);
static bool _OnRxOnTest(Serial_t serial, uint8_t b);

void Serial_Init(void)
{
    unsigned int i;
    Serial_Ctrl_t *ctrl;

    _actionBits = 0;

    // 初始化串口收发控制结构
    for(i = 0; i < ARRAY_LEN(_configs); i++)
    {
        ctrl = & _ctrls[i];

        ctrl->flags = 0;
        ctrl->uart = _configs[i].uart;
        RingBuffer_Init(& ctrl->rxRingBuffer, ctrl->_rxBuf, sizeof(ctrl->_rxBuf));
        RingBuffer_Init(& ctrl->txRingBuffer, ctrl->_txBuf, sizeof(ctrl->_txBuf));
        ctrl->onTxBegin = _DefaultCB;
        ctrl->onTxComplete = _DefaultCB;
        ctrl->onRxByte = _DefaultRxByteCB;
#ifdef CMSIS_OS
        ctrl->rxSem = osSemaphoreNew(SERIAL_RX_BUFFER_SIZE, 0, NULL);
#endif

        _configs[i].init();
        UART_ENABLE_INT(ctrl->uart, UART_INTEN_RDAIEN_Msk);
    }

    // 标准输入输出串口上，缓存满了以后，新的数据覆盖旧的数据 （RINGBUFFER_FLAG_OVERWRITE）
//    RingBuffer_SetFlags(& _ctrls[STDIO_SERIAL].rxRingBuffer, RINGBUFFER_FLAG_OVERWRITE);
//    RingBuffer_SetFlags(& _ctrls[STDIO_SERIAL].txRingBuffer, RINGBUFFER_FLAG_OVERWRITE);

#if SERIAL_HAVE_LPUART1
    LL_LPUART_EnableClockInStopMode(LPUART1);
    LL_LPUART_EnableInStopMode(LPUART1);
    LL_LPUART_Enable(LPUART1);
#endif
}

void Serial_DeInit(void)
{

}

static void _DefaultCB(Serial_t serial)
{
    UNUSED(serial);
}

static bool _DefaultRxByteCB(Serial_t serial, uint8_t b)
{
    UNUSED(serial);
    UNUSED(b);
    return false;
}

void Serial_SetCallbacks(
        Serial_t serial,
        Serial_Callback_t onTxBegin,
        Serial_Callback_t onTxComplete,
        Serial_RxByteCallback_t onRxByte)
{
    Serial_Ctrl_t *ctrl;

    assert_param(serial < Serial_Max);
    ctrl = & _ctrls[serial];

    if(onTxBegin != NULL)
    {
        ctrl->onTxBegin = onTxBegin;
    }

    if(onTxComplete != NULL)
    {
        ctrl->onTxComplete = onTxComplete;
    }

    if(onRxByte != NULL)
    {
        ctrl->onRxByte = onRxByte;
    }
}

void Serial_GetCallbacks(
        Serial_t serial,
        Serial_Callback_t *onTxBegin,
        Serial_Callback_t *onTxComplete,
        Serial_RxByteCallback_t *onRxByte)
{
    Serial_Ctrl_t *ctrl;

    assert(serial < Serial_Max);
    ctrl = & _ctrls[serial];

    if(onTxBegin != NULL)
    {
        *onTxBegin = ctrl->onTxBegin;
    }

    if(onTxComplete != NULL)
    {
        *onTxComplete = NULL;
    }

    if(onRxByte != NULL)
    {
        *onRxByte = ctrl->onRxByte;
    }
}

void Serial_SendByte(Serial_t serial, uint8_t b)
{
    Serial_Ctrl_t *ctrl;

//    assert(serial < Serial_Max);
    ctrl = & _ctrls[serial];
#if ENABLE_AUTO_NEW_LINE
    if((b == '\n') && (ctrl->flags & SERIAL_FLAG_AUTO_LINE))
    {
        Serial_SendByte(serial, '\r');
    }
#endif

    while(RingBuffer_Write(& ctrl->txRingBuffer, & b) != 0)
    {
        IDLE();
    }

    UART_ENABLE_INT(ctrl->uart, UART_INTEN_THREIEN_Msk);
}

void Serial_SendByteWithoutBuffer(Serial_t serial, uint8_t b)
{
    Serial_Ctrl_t *ctrl;

    ctrl = & _ctrls[serial];
#if ENABLE_AUTO_NEW_LINE
    if((b == '\n') && (ctrl->flags & SERIAL_FLAG_AUTO_LINE))
    {
        Serial_SendByteWithoutBuffer(serial, '\r');
    }
#endif

    while(! UART_GET_INT_FLAG(ctrl->uart, UART_INTSTS_THREIF_Msk))
    {
        __NOP();
    }

    UART_WRITE(ctrl->uart, b);
}

int Serial_RecvByteWithoutBuffer(Serial_t serial, uint8_t *byteOut)
{
    Serial_Ctrl_t *ctrl = & _ctrls[serial];

    if(UART_GET_INT_FLAG(ctrl->uart, UART_INTSTS_RDAIF_Msk))
    {
        *byteOut = (uint8_t)UART_READ(ctrl->uart);
        return 0;
    }
    else
    {
        return -1;
    }
}

void Serial_Send(Serial_t serial, const void *dataIn, unsigned int size)
{
    const uint8_t *vdata;
    unsigned int i;

    vdata = dataIn;
    for(i = 0; i < size; i++)
    {
        Serial_SendByte(serial, vdata[i]);
    }
}

void Serial_SendString(Serial_t serial, const char *str)
{
    char c;

    while(1)
    {
        c = *str++;
        if(c == '\0')
        {
            break;
        }

        Serial_SendByte(serial, c);
    }
}

void Serial_IRQHandler(Serial_t serial)
{
    Serial_Ctrl_t *ctrl;
    uint8_t b;

    ctrl = & _ctrls[serial];
    if(UART_GET_INT_FLAG(ctrl->uart, UART_INTSTS_RDAINT_Msk))
    {
        b = (uint8_t)UART_READ(ctrl->uart);
        if(ctrl->onRxByte(serial, b))
        {
            if(RingBuffer_Write(& ctrl->rxRingBuffer, & b) == 0)
            {
#ifdef CMSIS_OS
                osSemaphoreRelease(ctrl->rxSem);
#endif
            }
        }
    }

    if(UART_GET_INT_FLAG(ctrl->uart, UART_INTSTS_THREINT_Msk))
    {
        if(RingBuffer_Read(& ctrl->txRingBuffer, & b) == 0)
        {
            UART_WRITE(ctrl->uart, b);
        }
        else
        {
        	UART_DISABLE_INT(ctrl->uart, UART_INTEN_THREIEN_Msk);
        	ctrl->onTxComplete(serial);
        }
    }
}

int Serial_RecvByte(Serial_t serial, uint8_t *byteOut, unsigned int timeoutMs)
{
//    assert(serial < Serial_Max);
#ifdef CMSIS_OS
    int ret;

    ret = osSemaphoreAcquire(ctrl->rxSem, OS_TICK_MS(timeoutMs));
    if(ret != osOK)
    {
        return ret;
    }
#else
    UNUSED(timeoutMs);
#endif

    return RingBuffer_Read(& _ctrls[serial].rxRingBuffer, byteOut);
}

unsigned int Serial_Recv(Serial_t serial, void *data, unsigned int size, unsigned int timeoutMs)
{
    unsigned int i;
    uint8_t *vdata = data;

#ifdef CMSIS_OS
    for(i = 0; i < size; i++)
    {
        if(Serial_RecvByte(serial, &vdata[i], timeoutMs) != 0)
        {
            break;
        }
    }
#else
    ETimer_t timeout;

    ETimer_Set(&timeout, timeoutMs);
    for(i = 0; i < size; i++)
    {
        while(Serial_RecvByte(serial, &vdata[i], 0) != 0)
        {
            IDLE();
            if(ETimer_Expired(&timeout))
            {
                return i;
            }
        }
    }
#endif
    return i;
}

#if 0
/**
 *
 * @param pt
 * @param serial
 * @param dataOut
 * @param size
 * @param timeoutMs
 * @param statusOut
 * @return
 */
PT_THREAD(Serial_RecvThread(
        struct pt *pt,
        Serial_t serial,
        void *dataOut,
        unsigned int size,
        unsigned int timeoutMs,
        int *statusOut))
{
    static unsigned int i;
    static uint8_t *vdata;
    static Timer_t timeout;

    PT_BEGIN(pt);

    Timer_Set(& timeout, timeoutMs);

    vdata = dataOut;
    for(i = 0; i < size; i++)
    {
        PT_WAIT_UNTIL(pt,
                (Serial_RecvByte(serial, vdata) == 0) || Timer_Expired(& timeout));
        if(Timer_Expired(& timeout))
        {
            *statusOut = -1;
            PT_EXIT(pt);
        }

        vdata += 1;
    }

    *statusOut = 0;
    PT_EXIT(pt);

    PT_END(pt);
}
#endif

/**
 * @brief 从串口接收带回车结尾的一行字符
 * @param serial 串口序号
 * @param buf    字符缓存
 * @param size   字符缓存大小
 * @param end    行结尾符
 * @return 0  接收成功
 *         -1 接收失败
 */
int Serial_RecvLine(Serial_t serial, char buf[], unsigned int size, char end)
{
    size_t i;
    int ret;
    char c;
    rbuf_t *rxRbuf;

    rxRbuf = & _ctrls[serial].rxRingBuffer;
    ret = -1;
    for(i = 0; i < (size - 1); i++)
    {
        if(RingBuffer_Read(rxRbuf, & c) == -1)
        {
            break;
        }

        if(c == end)
        {
            ret = 0;
            break;
        }

        buf[i] = c;
    }

    buf[i] = '\0';
    return ret;
}

static bool _OnRxOnTest(Serial_t serial, uint8_t b)
{
    UNUSED(b);

    if((_ctrls[serial].flags & SERIAL_FLAG_TESTING) == 0)
    {
        BIT_SET(_ctrls[serial].flags, SERIAL_FLAG_TESTING);
        MsgQueue_Send(MsgQueue_Id_SerialTest, & serial, sizeof(serial));
    }

    return TRUE;
}

void Serial_TestCmd(Serial_t serial, bool isEnable)
{
    Serial_RxByteCallback_t cb;

    BIT_CLR(_ctrls[serial].flags, SERIAL_FLAG_TESTING);
    cb = isEnable ? _OnRxOnTest : _DefaultRxByteCB;
    Serial_SetCallbacks(serial, NULL, NULL, cb);
}

void Serial_OnTest(MsgQueue_Msg_t *msg)
{
    Serial_t serial;
    const char *name;
    char buf[64];
    unsigned int len;

    memcpy(& serial, msg->data, sizeof(serial));
#if 0
    ETimer_t timer;
    if(ctrl->onTxBegin != _DefaultCB)   // 大致判断是不是RS485串口
    {
        // 若是RS485串口，则等待一段时间再发以避免冲突
        ETimer_Set(& timer, 20);
        while(! ETimer_Expired(& timer));
    }
#endif

    len = Serial_Recv(serial, buf, sizeof(buf) - 1, 100);
    if(len != 0)
    {
        buf[len] = '\0';
        Serial_Send(serial, buf, len);
        Serial_IoCtl(serial, Serial_Ctl_GetName, &name);
        printf("%s RX: %s\n", name, buf);
    }

    BIT_CLR(_ctrls[serial].flags, SERIAL_FLAG_TESTING);
}

int Serial_IoCtl(int serial, int ctl, ...)
{
    Serial_t s;
    int ret = 0;
    va_list args;
    Serial_Ctrl_t *ctrl;

    if((serial < 0) || (serial >= Serial_Max))
    {
        return -1;
    }

    s = (Serial_t)serial;
    ctrl = &_ctrls[s];
    va_start(args, ctl);
    switch(ctl)
    {
    case Serial_Ctl_Enable:
        UART_ENABLE_INT(ctrl->uart, UART_INTEN_RDAIEN_Msk);
        break;

    case Serial_Ctl_Disable:
        UART_DISABLE_INT(ctrl->uart, UART_INTEN_RDAIEN_Msk);
        break;

    case Serial_Ctl_GetName:
    {
        const char **pName;

        pName = va_arg(args, const char **);
        *pName = _configs[serial].name;
    }
        break;

    case Serial_Ctl_SetFlags:
    {
        uint32_t flags;

        flags = va_arg(args, uint32_t);
        BIT_SET(ctrl->flags, flags);
    }
        break;

    case Serial_Ctl_ClearFlags:
    {
        uint32_t flags;

        flags = va_arg(args, uint32_t);
        BIT_CLR(ctrl->flags, flags);
    }
        break;

    case Serial_Ctl_ClearTxBuffer:
        RingBuffer_Clear(& ctrl->txRingBuffer);
        break;

    case Serial_Ctl_ClearRxBuffer:
        RingBuffer_Clear(& ctrl->rxRingBuffer);
        break;

//    case Serial_Ctl_GetBuadRate:
//    {
//        USART_TypeDef* uart = ctrl->uart;
//        LL_RCC_ClocksTypeDef RCC_Clocks;
//        uint32_t PeriphClk;
//        uint32_t OverSampling;
//        unsigned int *pRate;
//
//        pRate = va_arg(args, unsigned int *);
//        LL_RCC_GetSystemClocksFreq(&RCC_Clocks);
//        PeriphClk = RCC_Clocks.PCLK1_Frequency;
//        OverSampling = LL_USART_GetOverSampling(uart);
//    #if 0
//        *pRate = LL_USART_GetBaudRate(uart, PeriphClk, OverSampling);
//    #else
//        *pRate = LL_USART_GetBaudRate(uart, PeriphClk, OverSampling);
//    #endif
//    }
//        break;

//    case Serial_Ctl_RxCmd:
//    {
//        FunctionalState state;
//
//        state = (FunctionalState)va_arg(args, int);
//        if(state == DISABLE)
//        {
//            LL_USART_DisableIT_RXNE(ctrl->uart);
//        }
//        else
//        {
//            LL_USART_ClearFlag_NE(ctrl->uart);
//            LL_USART_EnableIT_RXNE(ctrl->uart);
//        }
//    }
//        break;

    case Serial_Ctl_IsTxDone:
    {
        ret = (int)UART_GET_INT_FLAG(ctrl->uart, UART_INTSTS_THREIF_Msk);
    }
        break;

    case Serial_Ctl_DeleteLastRxBufferByte:
        ret = RingBuffer_DeleteLast(&ctrl->rxRingBuffer);
        break;

//    case Serial_Ctl_GetUart:
//    {
//        USART_TypeDef **pUart;
//
//        pUart = va_arg(args, USART_TypeDef **);
//        *pUart = ctrl->uart;
//    }
//        break;

    case Serial_Ctl_SetDirection:
    {
        // M480的单线串口可自动切换RX、TX状态
#if 0
        Serial_Direction_t dir;

        dir = (Serial_Direction_t)va_arg(args, int);
        LL_USART_SetTransferDirection(ctrl->uart, dir);
#endif
    }
        break;

    default:
        ret = -2;
        break;
    }

    va_end(args);
    return  ret;
}

#if 0
int __io_putchar(int ch)
{
    Serial_SendByte(STDIO_SERIAL, (uint8_t)ch);
    return ch;
}

int __io_getchar(void)
{
    uint8_t b;
    int ret;

    do
    {
        ret = Serial_RecvByte(STDIO_SERIAL, &b);
    }while(ret != 0);

    return b;
}
#endif


static int _CmdSerial(void *stream, int argc, char *argv[])
{
    Serial_t s;

    if(argc < 3)
    {
        chprintf(stream, "Available Serial:\n");
        for(s = 0; s < Serial_Max; s++)
        {
            chprintf(stream, "%s: %d\n", _configs[s].name, (int)s);
        }

        return 0;
    }

    s = atoi(argv[1]);
    if((s < 0) || (s >= Serial_Max))
    {
        chprintf(stream, "Input Error!\n");
        return -1;
    }

    chprintf(stream, "Send %s to %s\n", argv[2], _configs[s].name);
    Serial_SendString(s, argv[2]);
    Serial_SendByte(s, '\n');

    return 0;
}

SHELL_CMD(serial, _CmdSerial, "Send message to a serial port.", "[<serial index> <message>]");
